home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d8
/
pdriver5.arc
/
3C503.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-12-17
|
38KB
|
1,084 lines
version equ 2
include defs.asm
;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec,
;* for the 3-Com 3C503 interface card.
;* Robert C Clements, K1BC, 14 February, 1989
;* Portions (C) Copyright 1988, 1989 Robert C Clements
;*
; Copyright, 1988, 1989, Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;* Change history:
;* Updated to driver spec version 1.08 Feb. 17, 1989 by Russell Nelson.
;* Changes 27 Jul 89 by Bob Clements (/Rcc)
;* Added Thick versus Thin Ethernet switch 27 Jul 89 by Bob Clements (/Rcc)
;* Added call to memory_test.
;* Added rcv_mode logic. Started, but didn't finish, multicast logic.
;* Fixed get_address to return current, not PROM, address.
;* Minor races fixed.
code segment byte public
assume cs:code, ds:code
HT equ 09h
CR equ 0dh
LF equ 0ah
;
; Packet Driver Error numbers
BAD_HANDLE equ 1 ;invalid handle number
NO_CLASS equ 2 ;no interfaces of specified class found
NO_TYPE equ 3 ;no interfaces of specified type found
NO_NUMBER equ 4 ;no interfaces of specified number found
BAD_TYPE equ 5 ;bad packet type specified
NO_MULTICAST equ 6 ;this interface does not support
CANT_TERMINATE equ 7 ;this packet driver cannot terminate
BAD_MODE equ 8 ;an invalid receiver mode was specified
NO_SPACE equ 9 ;operation failed because of insufficient
TYPE_INUSE equ 10 ;the type had previously been accessed,
BAD_COMMAND equ 11 ;the command was out of range, or not
CANT_SEND equ 12 ;the packet couldn't be sent (usually
; Stuff specific to the 3-Com 3C503 Ethernet controller board
; WD version in C by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
; 3Com version based on WD8003E version in .ASM, also by Bob Clements, dated
; 19 August 1988. The WD and 3Com cards both use the National DS8390.
; Symbol prefix "EN" is for Ethernet, National chip
; Symbol prefix "E33" is for _E_thernet, _3_Com 50_3_
; Symbol prefix "E33G" is for registers in the Gate array ASIC.
; The E33 registers - For the ASIC on the 3C503 card:
; Offsets from the board's base address, which can be set by
; jumpers to be one of the following 8 values (hex):
; 350, 330, 310, 300, 2E0, 2A0, 280, 250
; Factory default address is 300H.
; The card occupies a block of 16 I/O addresses.
; It also occupies 16 addresses at base+400 through base+40F.
; These high-addressed registers are in the ASIC.
; Recall that the normal PC I/O decoding is only 10 bits. The 11'th
; bit (400H) can be used on the same card for additional registers.
; This offset requires word, not byte, arithmetic
; on the DX register for the setport macro. Current SETPORT is OK.
; The card can also be jumpered to have the shared memory disabled
; or enabled at one of four addresses: C8000, CC000, D8000 or DC000.
; This version of the driver REQUIRES the shared memory to be
; enabled somewhere.
; The card can be operated using direct I/O instructions or by
; using the PC's DMA channels instead of the shared memory, but
; I haven't included the code for those other two methods.
; They would be needed in a system where all four possible addresses
; for the shared memory are in use by other devices. /Rcc
; Blocks of I/O addresses:
E33GA equ 400h ; Registers in the gate array.
E33_SAPROM equ 000h ; Window on station addr prom (if
; E33G_CNTRL bits 3,2 = 0,1
; The EN registers - the DS8390 chip registers
; These appear at Base+0 through Base+0F when bits 3,2 of
; E33G_CNTRL are 0,0.
; There are two (really 3) pages of registers in the chip. You select
; which page you want, then address them at offsets 00-0F from base.
; The chip command register (EN_CCMD) appears in both pages.
EN_CCMD equ 000h ; Chip's command register
; Page 0
EN0_STARTPG equ 001h ; Starting page of ring bfr
EN0_STOPPG equ 002h ; Ending page +1 of ring bfr
EN0_BOUNDARY equ 003h ; Boundary page of ring bfr
EN0_TSR equ 004h ; Transmit status reg
EN0_TPSR equ 004h ; Transmit starting page
EN0_TCNTLO equ 005h ; Low byte of tx byte count
EN0_TCNTHI equ 006h ; High byte of tx byte count
EN0_ISR equ 007h ; Interrupt status reg
EN0_RCNTLO equ 00ah ; Remote byte count reg
EN0_RCNTHI equ 00bh ; Remote byte count reg
EN0_RXCR equ 00ch ; RX control reg
EN0_TXCR equ 00dh ; TX control reg
EN0_COUNTER0 equ 00dh ; Rcv alignment error counter
EN0_DCFG equ 00eh ; Data configuration reg
EN0_COUNTER1 equ 00eh ; Rcv CRC error counter
EN0_IMR equ 00fh ; Interrupt mask reg
EN0_COUNTER2 equ 00fh ; Rcv missed frame error counter
; Page 1
EN1_PHYS equ 001h ; This board's physical enet addr
EN1_CURPAG equ 007h ; Current memory page
EN1_MULT equ 008h ; Multicast filter mask array (8 bytes)
; Chip commands in EN_CCMD
ENC_STOP equ 001h ; Stop the chip
ENC_START equ 002h ; Start the chip
ENC_TRANS equ 004h ; Transmit a frame
ENC_NODMA equ 020h ; No remote DMA used on this card
ENC_PAGE0 equ 000h ; Select page 0 of chip registers
ENC_PAGE1 equ 040h ; Select page 1 of chip registers
; Commands for RX control reg
ENRXCR_MON equ 020h ; Monitor mode (no packets rcvd)
ENRXCR_PROMP equ 010h ; Promiscuous physical addresses
ENRXCR_MULTI equ 008h ; Multicast (if pass filter)
ENRXCR_BCST equ 004h ; Accept broadcasts
ENRXCR_BAD equ 003h ; Accept runts and bad CRC frames
; Commands for TX control reg
ENTXCR_LOOP equ 002h ; Set loopback mode
; Bits in EN0_DCFG - Data config register
ENDCFG_BM8 equ 048h ; Set burst mode, 8 deep FIFO
; Bits in EN0_ISR - Interrupt status register
ENISR_RX equ 001h ; Receiver, no error
ENISR_TX equ 002h ; Transmitter, no error
ENISR_RX_ERR equ 004h ; Receiver, with error
ENISR_TX_ERR equ 008h ; Transmitter, with error
ENISR_OVER equ 010h ; Receiver overwrote the ring
ENISR_COUNTERS equ 020h ; Counters need emptying
ENISR_RESET equ 080h ; Reset completed
ENISR_ALL equ 03fh ; Interrupts we will enable
; Bits in received packet status byte and EN0_RSR
ENPS_RXOK equ 001h ; Received a good packet
; Bits in TX status reg
ENTSR_COLL equ 004h ; Collided at least once
ENTSR_COLL16 equ 008h ; Collided 16 times and was dropped
ENTSR_FU equ 020h ; TX FIFO Underrun
; Registers in the 3-Com custom Gate Array
E33G_STARTPG equ E33GA+00h ; Start page, must match EN0_STARTPG
E33G_STOPPG equ E33GA+01h ; Stop page, must match EN0_STOPPG
E33G_NBURST equ E33GA+02h ; Size of DMA burst before relinquishing bus
E33G_IOBASE equ E33GA+03h ; Bit coded: where I/O regs are jumpered.
; (Which you have to know already to read it)
E33G_ROMBASE equ E33GA+04h ; Bit coded: Where/whether EEPROM&DPRAM exist
E33G_GACFR equ E33GA+05h ; Config/setup bits for the ASIC GA
E33G_CNTRL equ E33GA+06h ; Board's main control register
E33G_STATUS equ E33GA+07h ; Status on completions.
E33G_IDCFR equ E33GA+08h ; Interrupt/DMA config register
; (Which IRQ to assert, DMA chan to use)
E33G_DMAAH equ E33GA+09h ; High byte of DMA address reg
E33G_DMAAL equ E33GA+0ah ; Low byte of DMA address reg
E33G_VP2 equ E33GA+0bh ; Vector pointer - for clearing RAM select
E33G_VP1 equ E33GA+0ch ; on a system reset, to re-enable EPROM.
E33G_VP0 equ E33GA+0dh ; 3Com says set this to Ctrl-Alt-Del handler
E33G_FIFOH equ E33GA+0eh ; FIFO for programmed I/O data moves ...
E33G_FIFOL equ E33GA+0fh ; .. low byte of above.
; Bits in E33G_CNTRL register:
ECNTRL_RESET equ 001h ; Software reset of the ASIC and 8390
ECNTRL_THIN equ 002h ; Onboard thin-net xcvr enable
ECNTRL_SAPROM equ 004h ; Map the station address prom
ECNTRL_DBLBFR equ 020h ; FIFO configuration bit
ECNTRL_OUTPUT equ 040h ; PC-to-3C501 direction if 1
ECNTRL_START equ 080h ; Start the DMA logic
; Bits in E33G_STATUS register:
ESTAT_DPRDY equ 080h ; Data port (of FIFO) ready
ESTAT_UFLW equ 040h ; Tried to read FIFO when it was empty
ESTAT_OFLW equ 020h ; Tried to write FIFO when it was full
ESTAT_DTC equ 010h ; Terminal Count from PC bus DMA logic
ESTAT_DIP equ 008h ; DMA In Progress
; Bits in E33G_GACFR register:
EGACFR_NORM equ 049h ; Enable 8K shared mem, no DMA TC int
EGACFR_IRQOFF equ 0c9h ; Above, and disable 8390 IRQ line
; Shared memory management parameters
XMIT_MTU equ 600h ; Largest packet we have room for.
SM_TSTART_PG equ 020h ; First page of TX buffer
SM_RSTART_PG equ 026h ; Starting page of RX ring
SM_RSTOP_PG equ 040h ; Last page +1 of RX ring
; Description of header of each packet in receive area of shared memory
EN_RBUF_STAT equ 0 ; Received frame status
EN_RBUF_NXT_PG equ 1 ; Page after this frame
EN_RBUF_SIZE_LO equ 2 ; Length of this frame
EN_RBUF_SIZE_HI equ 3 ; Length of this frame
EN_RBUF_NHDR equ 4 ; Length of above header area
; End of 3C503 parameter definitions
; The following two values may be overridden from the command line.
; If they are omitted from the command line, these defaults are used.
; The shared memory base is set by a jumper. We read it from the
; card and set up accordingly.
public int_no, io_addr, thick_or_thin
int_no db 2,0,0,0 ; Interrupt level
io_addr dw 0300h,0 ; I/O address for card (jumpers)
thick_or_thin dw 1,0 ; Non-zero means thin net
public mem_base
mem_base dw 00000h,0 ; Shared memory addr (jumpers)
; (Not changeable by software in 3C503) ; (0 if disabled by jumpers)
thin_bit db ECNTRL_THIN ; Default to thin cable
rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
public driver_class, driver_type, driver_name
driver_class db 1 ;from the packet spec
driver_type db 12 ;from the packet spec
driver_name db '3C503',0 ;name of the driver.
public card_hw_addr, curr_hw_addr, mcast_list_bits, mcast_all_flag
card_hw_addr db 0,0,0,0,0,0 ;Physical ethernet address
curr_hw_addr db 0,0,0,0,0,0 ;Address set into the 8390
mcast_list_bits db 0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
mcast_all_flag db 0 ;Non-zero if hware should have all
; ones in mask rather than this list.
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4
dw rcv_mode_5
dw rcv_mode_6
; send_pkt: - The Transmit Frame routine
public send_pkt
send_pkt:
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
loadport ; Point at chip command register
setport EN_CCMD ; ..
mov bx, 8000h ; Avoid infinite loop
tx_wait:
in al, dx ; Get chip command state
test al,ENC_TRANS ; Is transmitter still running?
jz tx_idle ; Go if free
dec bx ; Count the timeout
jnz tx_wait ; Fall thru if TX is stuck
; Should count these error timeouts
; Maybe need to add recovery logic here
tx_idle:
cmp cx,XMIT_MTU ; Is this packet too large?
ja send_pkt_toobig
cmp cx, RUNT ; Is the frame long enough?
jnb tx_oklen ; Go if OK
mov cx, RUNT ; Stretch frame to minimum allowed
tx_oklen:
push cx ; Hold count for later
loadport ; Set up for address of TX buffer in
setport E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_NORM ; the RAM is enabled (not EPROM)
out dx, al ; ..
mov ax, cs:mem_base ; Set up ES at the shared RAM
mov es, ax ; ..
xor ax, ax ; Set up DI at base of tx buffer
mov ah, SM_TSTART_PG ; Where to put tx frame
mov di, ax ; ..
call movemem
pop cx ; Get back count to give to board
setport EN0_TCNTLO ; Low byte of TX count
mov al, cl ; Get the count
out dx, al ; Tell card the count
setport EN0_TCNTHI ; High byte of TX count
mov al, ch ; Get the count
out dx, al ; Tell card the count
setport EN0_TPSR ; Transmit Page Start Register
mov al, SM_TSTART_PG
out dx, al ; Start the transmitter
setport EN_CCMD ; Chip command reg
mov al, ENC_TRANS+ENC_NODMA
out dx, al ; Start the transmitter
clc ; Successfully started
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
ret
movemem:
;does the same thing as "rep movsb", only 50% faster.
;moves words instead of bytes, and handles the case of both addresses odd
;efficiently. There is no way to handle one address odd efficiently.
;This routine always aligns the source address in the hopes that the
;destination address will also get aligned. This is from Phil Karn's
;code from ec.c, a part of his NET package. I bummed a few instructions
;out.
jcxz movemem_cnte ; If zero, we're done already.
test si,1 ; Does source start on odd byte?
jz movemem_adre ; Go if not
movsb ; Yes, move the first byte
dec cx ; Count that byte
movemem_adre:
shr cx,1 ; convert to word count
rep movsw ; Move the bulk as words
jnc movemem_cnte ; Go if the count was even
movsb ; Move leftover last byte
movemem_cnte:
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
; Give caller the one currently in the 8390, not necessarily the one in PROM.
assume ds:code
cmp cx, EADDR_LEN ; Caller wants a reasonable length?
jb get_addr_x ; No, fail.
mov cx, EADDR_LEN ; Move one ethernet address from our copy
mov si, offset curr_hw_addr ; Copy from most recent setting
rep movsb
mov cx, EADDR_LEN ; Tell caller how many bytes we fed him
clc ; Carry off says success
ret
get_addr_x:
stc ; Tell caller our addr is too big for him
ret
public set_address
set_address:
assume ds:nothing
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
;
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
push cs ; Copy from them to our RAM copy
pop es ; Destination of move
mov di, offset curr_hw_addr
rep movsb ; Move their address
call set_8390_eaddr ; Put that address in the chip
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
; Copy our Ethernet address from curr_hw_addr into the DS8390
set_8390_eaddr:
push cs ; Get it from our local RAM copy
pop ds
mov si, offset curr_hw_addr
mov cx, EADDR_LEN ; Move one ethernet address from our copy
loadport
setport EN_CCMD ; Chip command register
sti ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1
out dx, al ; Switch to page one for writing eaddr
setport EN1_PHYS ; Where it goes in 8390
set_8390_1:
lodsb
out dx,al
inc dx
loop set_8390_1
loadport
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Restore to page zero
cli ; OK for interrupts now
ret
; Routines to set address filtering modes in the DS8390
rcv_mode_1: ; Turn off receiver
mov al, ENRXCR_MON ; Set to monitor for counts but accept none
jmp short rcv_mode_set
rcv_mode_2: ; Receive only packets to this interface
mov al, 0 ; Set for only our packets
jmp short rcv_mode_set
rcv_mode_3: ; Mode 2 plus broadcast packets (This is the default)
mov al, ENRXCR_BCST ; Set four ours plus broadcasts
jmp short rcv_mode_set
rcv_mode_4: ; Mode 3 plus selected multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,0
jmp short rcv_mode_set
rcv_mode_5: ; Mode 3 plus ALL multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,1
jmp short rcv_mode_set
rcv_mode_6: ; Receive all packets (Promiscuous physical plus all multi)
mov al, ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
mov mcast_all_flag,1
rcv_mode_set:
push ax ; Hold mode until masks are right
call set_8390_multi ; Set the multicast mask bits in chip
pop ax
loadport
setport EN0_RXCR ; Set receiver to selected mode
out dx, al
mov rxcr_bits,al ; Save a copy of what we set it to
ret
public set_multicast_list
set_multicast_list:
;enter with es:di ->list of multicast addresses, cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
mov dh,NO_MULTICAST
stc
ret
; Set the multicast filter mask bits in case promiscuous rcv wanted
set_8390_multi:
loadport
setport EN_CCMD ; Chip command register
mov cx, 8 ; Eight bytes of multicast filter
mov si, offset mcast_list_bits ; Where bits are, if not all ones
push cs
pop ds
sti ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1
out dx, al ; Switch to page one for writing eaddr
setport EN1_MULT ; Where it goes in 8390
mov al, mcast_all_flag ; Want all ones or just selected bits?
or al, al
je set_mcast_2 ; Just selected ones
mov al, 0ffh ; Ones for filter
set_mcast_all:
out dx, al ; Write a mask byte
inc dl ; Step to next one
loop set_mcast_all ; ..
jmp short set_mcast_x
set_mcast_2:
lodsb ; Get a byte of mask bits
out dx, al ; Write a mask byte
inc dl ; Step to next I/O register
loop set_mcast_2 ; ..
set_mcast_x:
loadport
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Restore to page zero
cli ; OK for interrupts now
ret
public reset_interface
reset_interface:
assume ds:code
loadport ; Base of I/O regs
setport EN_CCMD ; Chip command reg
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Stop the DS8390
setport EN0_ISR ; Interrupt status reg
mov al, 0ffh ; Clear all pending interrupts
out dx, al ; ..
setport EN0_IMR ; Interrupt mask reg
xor al, al ; Turn off all enables
out dx, al ; ..
ret
; Linkages to non-device-specific routines
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
;It returns with es:di = 0 if don't want this type or if no buffer available.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Actually, not just receive, but all interrupts come here.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
check_isr: ; Was there an interrupt from this card?
loadport ; Point at card's I/O port base
setport E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_NORM ; the RAM is enabled (not EPROM)
out dx, al ; ..
setport EN0_ISR ; Point at interrupt status register
in al, dx ; Get pending interrupts
and al, ENISR_ALL ; Any?
jnz isr_test_overrun
jmp interrupt_done ; Go if none
; First, a messy procedure for handling the case where the rcvr
; over-runs its ring buffer. This is spec'ed by National for the chip.
; This is handled differently in sample code from 3Com and from WD.
; This is close to the WD version. May need tweaking if it doesn't
; work for the 3Com card.
isr_test_overrun:
test al,ENISR_OVER ; Was there an overrun?
jnz recv_overrun ; Go if so.
jmp recv_no_overrun ; Go if not.
recv_overrun:
setport EN_CCMD ; Stop the chip
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Write "stop" to command register
; Remove one frame from the ring
setport EN0_BOUNDARY ; Find end of this frame
in al, dx ; Get memory page number
inc al ; Page plus 1
cmp al, SM_RSTOP_PG ; Wrapped around ring?
jnz rcv_ovr_nwrap ; Go if not
mov al, SM_RSTART_PG ; Yes, wrap the page pointer
rcv_ovr_nwrap:
xor ah, ah ; Convert page to segment
mov cl, 4
mov bl, al ; Page number as arg to rcv_frm
shl ax, cl ; ..
add ax, mem_base ; Page in this memory
mov es, ax ; Segment pointer to the frame header
push es ; Hold this frame pointer for later
mov al, es:[EN_RBUF_STAT] ; Get the buffer status byte
test al,ENPS_RXOK ; Is this frame any good?
jz rcv_ovr_ng ; Skip if not
call rcv_frm ; Yes, go accept it
rcv_ovr_ng:
pop es ; Back to start of this frame
mov al, es:[EN_RBUF_NXT_PG] ; Get pointer to next frame
dec al ; Back up one page
cmp al, SM_RSTART_PG ; Did it wrap?
jge rcv_ovr_nwr2
mov al, SM_RSTOP_PG-1 ; Yes, back to end of ring
rcv_ovr_nwr2:
loadport ; Point at boundary reg
setport EN0_BOUNDARY ; ..
out dx, al ; Set the boundary
setport EN0_RCNTLO ; Point at byte count regs
xor al, al ; Clear them
out dx, al ; ..
setport EN0_RCNTHI
out dx, al
setport EN0_ISR ; Point at status reg
mov cx, 8000h ; Timeout counter
rcv_ovr_rst_loop:
in al, dx ; Is it finished resetting?
test al,ENISR_RESET ; ..
jnz rcv_ovr_rst ; Go if so
dec cx ; Loop til reset, or til timeout
jnz rcv_ovr_rst_loop
rcv_ovr_rst:
loadport ; Point at Transmit control reg
setport EN0_TXCR ; ..
mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
out dx, al ; ..
setport EN_CCMD ; Point at Chip command reg
mov al, ENC_START+ENC_NODMA
out dx, al ; Start the chip running again
setport EN0_TXCR ; Back to TX control reg
xor al, al ; Clear the loopback bit
out dx, al ; ..
setport EN0_ISR ; Point at Interrupt status register
mov al, ENISR_OVER ; Clear the overrun interrupt bit
out dx, al ; ..
call count_in_err ; Count the anomaly
jmp check_isr ; Done with the overrun case
recv_no_overrun:
; Handle receive flags, normal and with error (but not overrun).
test al,ENISR_RX+ENISR_RX_ERR ; Frame received without overrun?
jnz recv_frame ; Go if so.
jmp recv_no_frame ; Go if not.
recv_frame:
loadport ; Point at Chip's Command Reg
setport EN_CCMD ; ..
mov al, ENC_NODMA+ENC_PAGE1
out dx, al ; Switch to page 1 registers
setport EN1_CURPAG ;Get current page of rcv ring
in al, dx ; ..
mov ah, al ; Hold current page in AH
setport EN_CCMD ; Back to page zero registers
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Switch back to page 0 registers
setport EN0_BOUNDARY ;Get boundary page
in al, dx ; ..
inc al ; Step boundary from last used page
cmp al, SM_RSTOP_PG ; Wrap if needed
jne rx_nwrap3 ; Go if not
mov al, SM_RSTART_PG ; Wrap to first RX page
rx_nwrap3:
cmp al, ah ; Read all the frames?
je recv_frame_break ; Finished them all
mov bl, al ; Page number as arg to rcv_frm
xor ah, ah ; Make segment pointer to this frame
mov cl, 4 ; 16 * pages = paragraphs
shl ax, cl ; ..
add ax, mem_base ; That far into shared memory
mov es, ax ; Segment part of pointer
push es ; Hold on to this pointer for later
mov al, es:[EN_RBUF_STAT] ; Get the buffer status byte
test al,ENPS_RXOK ; Good frame?
jz recv_no_rcv
call rcv_frm ; Yes, go accept it
recv_no_rcv:
pop es ; Back to base of frame
mov al, es:[EN_RBUF_NXT_PG] ; Start of next frame
dec al ; Make previous page for new boundary
cmp al, SM_RSTART_PG ; Wrap around the bottom?
jge rcv_nwrap4
mov al, SM_RSTOP_PG-1 ; Yes
rcv_nwrap4:
loadport ; Point at the Boundary Reg again
setport EN0_BOUNDARY ; ..
out dx, al ; Set new boundary
jmp recv_frame ; See if any more frames
recv_frame_break:
loadport ; Point at Interrupt Status Reg
setport EN0_ISR ; ..
mov al, ENISR_RX+ENISR_RX_ERR+ENISR_OVER
out dx, al ; Clear those requests
jmp check_isr ; See if any other interrupts pending
recv_no_frame: ; Handle transmit flags.
test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
jnz isr_tx ; Go if so.
jmp isr_no_tx ; Go if not.
isr_tx:
mov ah, al ; Hold interrupt status bits
loadport ; Point at Transmit Status Reg
setport EN0_TSR ; ..
in al, dx ; ..
test ah,ENISR_TX ; Non-error TX?
jz isr_tx_err ; No, do TX error completion
test al,ENTSR_COLL16 ; Jammed for 16 transmit tries?
jz isr_tx_njam ; Go if not
call count_out_err ; Yes, count those
isr_tx_njam:
setport EN0_ISR ; Clear the TX complete flag
mov al, ENISR_TX ; ..
out dx, al ; ..
jmp isr_tx_done
isr_tx_err:
test al,ENTSR_FU ; FIFO Underrun?
jz isr_txerr_nfu
call count_out_err ; Yes, count those
isr_txerr_nfu:
loadport ; Clear the TX error completion flag
setport EN0_ISR ; ..
mov al, ENISR_TX_ERR ; ..
out dx, al ; ..
isr_tx_done:
; If TX queue and/or TX shared memory ring buffer were being
; used, logic to step through them would go here. However,
; in this version, we just clear the flags for background to notice.
jmp check_isr ; See if any other interrupts on
isr_no_tx:
; Now check to see if any counters are getting full
test al,ENISR_COUNTERS ; Interrupt to handle counters?
jnz isr_stat ; Go if so.
jmp isr_no_stat ; Go if not.
isr_stat:
; We have to read the counters to clear them and to clear the interrupt.
; Version 1 of the PC/FTP driver spec doesn't give us
; anything useful to do with the data, though.
; Fix this up for V2 one of these days.
loadport ; Point at first counter
setport EN0_COUNTER0 ; ..
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER1
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER2
in al, dx ; Read the count, ignore it.
setport EN0_ISR ; Clear the statistics completion flag
mov al, ENISR_COUNTERS ; ..
out dx, al ; ..
isr_no_stat:
jmp check_isr ; Anything else to do?
interrupt_done:
ret
; Do the work of copying out a receive frame.
; Called with bl/ the page number of the frame header in shared memory/
; Also, es/ the paragraph number of that page.
rcv_frm:
; Old version checked size, memory space, queue length here. Now done
; in higher level code.
; Set cx to length of this frame.
mov ch, es:[EN_RBUF_SIZE_HI] ; Extract size of frame
mov cl, es:[EN_RBUF_SIZE_LO] ; Extract size of frame
sub cx, EN_RBUF_NHDR ; Less the header stuff
; Set es:di to point to Ethernet type field. es is already at base of
; page where this frame starts. Set di after the header and two addresses.
mov di, EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
push bx ; Save page number in bl
push cx ; Save frame size
push es
mov ax, cs ; Set ds = code
mov ds, ax
assume ds:code
call recv_find ; See if type and size are wanted
pop ds ; RX page pointer in ds now
assume ds:nothing
pop cx
pop bx
cld ; Copies below are forward, please
mov ax, es ; Did recv_find give us a null pointer?
or ax, di ; ..
je rcv_no_copy ; If null, don't copy the data
push cx ; We will want the count and pointer
push es ; to hand to client after copying,
push di ; so save them at this point
;; if ( (((size + 255 + EN_RBUF_NHDR) >> 8) + pg) > SM_RSTOP_PG){
mov ax, cx ; Length of frame
add ax, EN_RBUF_NHDR+255 ; Including the overhead bytes, rounded up
add ah, bl ; Compute page with last byte of data in ah
cmp ah, SM_RSTOP_PG ; Over the top of the ring?
jg rcopy_wrap ; Yes, move in two pieces
mov si, EN_RBUF_NHDR ; One piece, starts here in first page (in ds)
jmp rcopy_one_piece ; Go move it
rcopy_wrap:
;; Copy in two pieces due to buffer wraparound. */
;; n = ((SM_RSTOP_PG - pg) << 8) - EN_RBUF_NHDR; /* To top of mem */
mov ah, SM_RSTOP_PG ; Compute length of first part
sub ah, bl ; as all of the pages up to wrap point
xor al, al ; 16-bit count
sub ax, EN_RBUF_NHDR ; Less the four overhead bytes
sub cx, ax ; Move the rest in second part
push cx ; Save count of second part
mov cx, ax ; Count for first move
mov si, EN_RBUF_NHDR ; ds:si points at first byte to move
shr cx, 1 ; All above are even numbers, do words.
rep movsw ; Move first part of frame
mov ax, mem_base ; Paragraph of base of shared memory
mov ds, ax ; ..
mov si, SM_RSTART_PG*256 ; Offset to start of first receive page
pop cx ; Bytes left to move
rcopy_one_piece:
call movemem
pop si ; Recover pointer to destination
pop ds ; Tell client it's his source
pop cx ; And it's this long
assume ds:nothing
call recv_copy ; Give it to him
rcv_no_copy:
push cs ; Put ds back in code space
pop ds ; ..
assume ds:code
ret ; That's it for rcv_frm
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: 3C503 <packet_int_no> <int_level(2-5)> <io_addr> <thin_net_flag>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for 3-Com 3C503, version ",'0'+majver,".",'0'+version,CR,LF
db "Portions Copyright 1989, Robert C. Clements, K1BC",CR,LF,'$'
cfg_err_msg:
db "3C503 Configuration failed. Check parameters.",CR,LF,'$'
no_mem_msg:
db "3C503 memory jumper must be set to enable memory.",CR,LF
db "Driver cannot run with memory disabled.",'$'
int_no_name:
db "Interrupt number ",'$'
io_addr_name:
db "I/O port ",'$'
mem_base_name:
db "Memory address ",'$'
thick_thin_msg:
db "Flag, non-zero if thin Ethernet: ",'$'
mem_busted_msg:
db "Shared RAM on 3C503 card is defective or there is an address conflict.",CR,LF,'$'
extrn set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
public parse_args
parse_args:
mov di, offset int_no ; May override interrupt channel
mov bx, offset int_no_name ; Message for it
call get_number
mov di, offset io_addr ; May override I/O address
mov bx, offset io_addr_name ; Message for it
call get_number
mov di, offset thick_or_thin ; May override thick/thin cable flag
mov bx, offset thick_thin_msg ; Message for it
call get_number
mov ax, thick_or_thin ; Now make the right bit
cmp ax, 0
je parse_thin1 ; If zero, leave bit off
mov al, ECNTRL_THIN ; Else the bit for the card
parse_thin1:
mov thin_bit,al ; Save for setting up the card
; mov di, offset mem_base ; Not movable in 3C503
; mov bx, offset mem_base_name ; Message for it
; call get_number ; Must get from jumpers.
ret
mem_busted:
mov dx, offset mem_busted_msg
jmp short error
nomem_error:
mov dx,offset no_mem_msg
jmp short error
cfg_error:
mov dx,offset cfg_err_msg
error:
mov ah,9 ; Type the msg
int 21h
stc ; Indicate error
ret ; Return to common code
; Called once to initialize the 3C503 card
extrn memory_test: near
public etopen
etopen: ; Initialize interface
; First, initialize the Gate Array (ASIC) card logic. Later do the 8390.
loadport ; First, pulse the board reset
setport E33G_CNTRL
mov al, thin_bit ; Thick or thin cable bit
or al, ECNTRL_RESET
out dx, al ; Turn on board reset bit
and al, ECNTRL_THIN
out dx, al ; Turn off board reset bit
; Now get the board's physical address from on-board PROM into card_hw_addr
sti ; Protect the E33G_CNTRL contents
setport E33G_CNTRL ; Switch control bits to enable SA PROM
mov al, thin_bit
or al, ECNTRL_SAPROM
out dx, al ; ..
setport E33_SAPROM ; Where the address prom is
cld ; Make sure string mode is right
push cs ; Point es:di at local copy space
pop es
mov di, offset card_hw_addr
mov cx, EADDR_LEN ; Set count for loop
ini_addr_loop:
in al, dx ; Get a byte of address
stosb ; Feed it to caller
inc dx ; Next byte at next I/O port
loop ini_addr_loop ; Loop over six bytes
loadport ; Re-establish I/O base after dx mods
setport E33G_CNTRL ; Switch control bits to turn off SA PROM
mov al, thin_bit
out dx, al ; Turn off SA PROM windowing
cli ; Ok for E33G_CNTRL to change now
; Point the "Vector Pointer" registers off into the boonies so we
; don't get the shared RAM disabled on us while we're using it.
; Ideally a warm boot should reset this too, to get to ROM on this card,
; but I don't know a guaranteed way to determine that value.
setport E33G_VP2
mov al, 0ffh ; Point this at the ROM restart location
out dx, al ; of ffff0h.
setport E33G_VP1
out dx, al
xor al, al
setport E33G_VP0
out dx, al
;Make sure shared memory is jumpered on. Find its address.
setport E33G_ROMBASE ; Point at rom/ram cfg reg
in al, dx ; Read it
test al,0f0h ; Any bits on?
jne memcfg_1 ; Yes
jmp nomem_error ; No, can't run without it
memcfg_1:
mov bx, 0c600h ; Build mem segment here
test al,0c0h ; DC00 or D800?
je memcfg_2 ; No
add bx, 01000h ; Yes, make Dx00
memcfg_2:
test al,0a0h ; DC00 or CC00?
je memcfg_3
add bx, 00400h ; Yes, make xC00
memcfg_3:
mov mem_base,bx ; Remember segment addr of memory
; Set up Gate Array's Config Reg to enable and size the RAM.
setport E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_IRQOFF ; the RAM is enabled (not EPROM)
out dx, al ; ..
; Check the card's memory
mov ax, mem_base ; Set segment of the shared memory
add ax, 16*SM_TSTART_PG ; which starts 2000h up from "base"
mov cx, 2000h ; Length of RAM to test
call memory_test ; Check it out
jz mem_works ; Go if it's OK
jmp mem_busted ; Go report failure if it's bad
mem_works:
; Set up control of shared memory, buffer ring, etc.
loadport
setport E33G_STARTPG ; Set ASIC copy of rx's first buffer page
mov al, SM_RSTART_PG
out dx, al
setport E33G_STOPPG ; and ASIC copy of rx's last buffer page + 1
mov al, SM_RSTOP_PG
out dx, al
; Set up interrupt/DMA control register in ASIC.
; For now, we won't use the DMA, so B0-B3 are zero.
xor ah, ah ; Get the interrupt level from arg line
mov al, int_no ; ..
cmp al, 9 ; If converted to 9, make back into 2
jne get_irq1 ; Not 9
mov al, 2 ; Card thinks it's IRQ2
get_irq1: ; Now should have level in range 2-5
sub ax, 2 ; Make 0-3 for tables
cmp ax, 5-2 ; In range?
jna get_irq2
jmp cfg_error ; If not, can't configure.
get_irq2:
xor cx, cx ; Make the bit for the ASIC
mov cl, al ; Shift count
mov al, 10h ; Bit for irq2
jcxz get_irq3 ; Go if it's 2
shl al, cl ; Shift over for 3-5
get_irq3:
setport E33G_IDCFR ; Point at ASIC reg for IRQ level
out dx, al ; Set the bit
setport E33G_NBURST ; Set burst size to 8
mov al, 8
out dx, al ; ..
setport E33G_DMAAH ; Set up transmit bfr in DMA addr
mov al, SM_TSTART_PG
out dx, al
xor ax, ax
setport E33G_DMAAL
out dx, al
; Now, initialize the DS8390 Ethernet Controller chip
ini_8390:
setport EN_CCMD ; DS8390 chip's command register
mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
out dx, al ; Switch to page zero
setport EN0_ISR ; Clear all interrupt flags
mov al, 0ffh ; ..
out dx, al ; ..
setport EN0_DCFG ; Configure the fifo organization
mov al, ENDCFG_BM8 ; Fifo threshold = 8 bytes
out dx, al
setport EN0_TXCR ; Set transmitter mode to normal
xor al, al
out dx, al
setport EN0_RXCR ; Set receiver to monitor mode
mov al, ENRXCR_MON
out dx, al
; Set up control of shared memory, buffer ring, etc.
setport EN0_STARTPG ; Set receiver's first buffer page
mov al, SM_RSTART_PG
out dx, al
setport EN0_STOPPG ; and receiver's last buffer page + 1
mov al, SM_RSTOP_PG
out dx, al
setport EN0_BOUNDARY ; Set initial "last page we have emptied"
mov al, SM_RSTART_PG ; (WD doc says set to RSTART_PG)
; dec al ; (3Com doc says set to RSTOP_PG-1 ?)
; ; (and 3Com handling of BOUNDARY is
; ; different throughout.)
out dx, al ; (Check out why WD and 3Com disagree)
push ds ; Copy from card's address to current address
pop es
mov si, offset card_hw_addr
mov di, offset curr_hw_addr
mov cx, EADDR_LEN ; Copy one address length
rep movsb ; ..
call set_8390_eaddr ; Now set the address in the 8390 chip
call set_8390_multi ; Put the right stuff into 8390's multicast masks
loadport ; Base of I/O regs
setport EN1_CURPAG ; Set current shared page for RX to work on
mov al, SM_RSTART_PG+1
out dx, al
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Back to page zero
setport EN0_RCNTLO ; Clear the byte count registers
xor al, al ; ..
out dx, al
setport EN0_RCNTHI
out dx, al ; Clear high byte, too
setport EN0_IMR ; Clear all interrupt enable flags
xor al, al
out dx, al
setport EN0_ISR ; Clear all interrupt assertion flags
mov al, 0ffh ; again for safety before making the
out dx, al ; interrupt be enabled
call set_recv_isr ; Put ourselves in interrupt chain
loadport
setport EN_CCMD ; Now start the DS8390
mov al, ENC_START+ENC_NODMA
out dx, al ; interrupt be enabled
setport EN0_RXCR ; Tell it what frames to accept
mov al, rxcr_bits ; As most recently set by set_mode
out dx, al
setport E33G_GACFR ; Now let it interrupt us
mov al, EGACFR_NORM ; and leave RAM enabled
out dx, al
setport EN0_IMR ; Tell card it can cause these interrupts
mov al, ENISR_ALL
out dx, al
mov dx, offset end_resident ; Report our size
clc ; Say no error
ret ; Back to common code
code ends
end